package com.ashlikun.okhttputils.http.extend

import com.ashlikun.okhttputils.http.request.ContentRequestBody
import com.ashlikun.okhttputils.http.request.FileInput
import com.ashlikun.okhttputils.http.request.ProgressRequestBody
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializer
import com.google.gson.reflect.TypeToken
import okhttp3.FormBody
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.MultipartBody.Part
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.internal.checkOffsetAndCount
import okio.BufferedSink
import okio.ByteString
import java.io.File
import java.nio.charset.Charset
import kotlin.text.Charsets.UTF_8

/**
 * 作者　　: 李坤
 * 创建时间: 2024/10/31　14:37
 * 邮箱　　：496546144@qq.com
 *
 * 功能介绍：Body 相关扩展方法
 */
fun String.toRequestBodyNoContentLength(contentType: MediaType? = null): RequestBody {
    var charset: Charset = UTF_8
    var finalContentType: MediaType? = contentType
    if (contentType != null) {
        val resolvedCharset = contentType.charset()
        if (resolvedCharset == null) {
            charset = UTF_8
            finalContentType = "$contentType; charset=utf-8".toMediaTypeOrNull()
        } else {
            charset = resolvedCharset
        }
    }
    val bytes = toByteArray(charset)
    return bytes.toRequestBodyNoContentLength(finalContentType, 0, bytes.size)
}

fun ByteString.toRequestBodyNoContentLength(contentType: MediaType? = null): RequestBody {
    return object : RequestBody() {
        override fun contentType() = contentType

        override fun writeTo(sink: BufferedSink) {
            sink.write(this@toRequestBodyNoContentLength)
        }
    }
}

/** 返回一个新的请求正文，用于传输以下内容. */
fun ByteArray.toRequestBodyNoContentLength(contentType: MediaType? = null, offset: Int = 0, byteCount: Int = size): RequestBody {
    checkOffsetAndCount(size.toLong(), offset.toLong(), byteCount.toLong())
    return object : RequestBody() {
        override fun contentType() = contentType
        override fun writeTo(sink: BufferedSink) {
            sink.write(this@toRequestBodyNoContentLength, offset, byteCount)
        }
    }
}

/**
 * FormBody 转 Map
 */
fun FormBody.toMap(): Map<String, String> {
    val newMap = HashMap<String, String>()
    (0 until size).forEach {
        newMap[name(it)] = value(it)
    }
    return newMap
}

/**
 * 直接添加Map
 */
fun FormBody.Builder.addMap(map: Map<String, Any>): FormBody.Builder {
    map.forEach { add(it.key, it.value.toString()) }
    return this
}

/**
 * 直接添加Map
 */
fun FormBody.Builder.addEncodedMap(map: Map<String, Any>): FormBody.Builder {
    map.forEach { addEncoded(it.key, it.value.toString()) }
    return this
}

/**
 * 修改或者添加参数
 */
fun ContentRequestBody.setRequestParams(data: Map<String, Any>, isAdd: Boolean = true): ContentRequestBody {
    //data方式提交，一般是json
    var content = getContent()
    runCatching { //处理Number数据格式化异常
        val gson = GsonBuilder().registerTypeAdapter(object : TypeToken<Map<String?, Any?>?>() {}.type, JsonDeserializer<Map<String, Any>> { json, typeOfT, context ->
            val treeMap: MutableMap<String, Any> = HashMap()
            val jsonObject = json.asJsonObject
            val entrySet = jsonObject.entrySet()
            for ((key1, value1) in entrySet) {
                treeMap[key1] = value1
            }
            treeMap
        }).create()
        val p = gson.fromJson<MutableMap<String, Any>>(content, object : TypeToken<Map<String?, Any?>?>() {}.type)
        data.forEach { item ->
            if (isAdd) {
                p[item.key] = item.value
            } else if (p.containsKey(item.key)) {
                p[item.key] = item.value
            }
        }
        content = gson.toJson(p)
    }.onFailure { it.printStackTrace() }
    return ContentRequestBody.createNew(contentType(), content)
}

/**
 * 修改或者添加参数
 */
fun FormBody.setRequestParams(data: Map<String, Any>, isAdd: Boolean = true): FormBody.Builder {
    //表单提交
    val newMap = toMap().toMutableMap()
    data.forEach { item ->
        if (isAdd) {
            newMap[item.key] = item.value.toString()
        } else {
            if (newMap.containsKey(item.key)) newMap[item.key] = item.value.toString()
        }
    }
    return FormBody.Builder().addMap(newMap)
}

/**
 * 修改或者添加参数
 */
fun MultipartBody.setRequestParams(data: Map<String, Any>, isAdd: Boolean = true): MultipartBody.Builder {
    //复杂表单
    val newList = parts.toMutableList()
    data.forEach { item ->
        if (isAdd) {
            newList.add(Part.createFormData(item.key, item.value.toString()))
        } else {
            //form-data; name=aaa
            val oldPart = newList.find { it.headers?.get("Content-Disposition")?.contains(item.key) == true }
            if (oldPart != null) {
                newList.remove(oldPart)
                val disposition = oldPart.headers?.get("Content-Disposition")
                if (disposition?.contains("filename") == true) {
                    //文件
                    if (item.value is FileInput) {
                        newList.add((item.value as FileInput).toPart())
                    } else {
                        val file = if (item.value is File) item.value as File else File(item.value.toString())
                        if (file != null) {
                            val fileInput = FileInput(item.key, file)
                            newList.add(fileInput.toPart())
                        }
                    }
                } else {
                    newList.add(Part.createFormData(item.key, item.value.toString()))
                }
            }
        }
    }
    return MultipartBody.Builder().also {
        parts.forEach { pa ->
            it.addPart(pa)
        }
    }
}


/**
 * 查询设置请求参数
 * @param isAdd 是否直接添加，不管是否存在，false:只有已经存在的才会赋值
 */
fun RequestBody.setParams(data: Map<String, Any>, isAdd: Boolean = true): RequestBody? {
    if (data.isEmpty()) return null
    return when (val body = this) {
        is ContentRequestBody -> body.setRequestParams(data, isAdd)
        is FormBody -> body.setRequestParams(data, isAdd).build()
        is MultipartBody -> body.setRequestParams(data, isAdd).build()
        is ProgressRequestBody -> {
            //带进度
            return body.requestBody.setParams(data, isAdd)?.let { ProgressRequestBody(it, body.rate, body.progressCallBack) }
        }

        else -> null
    }
}
